struct Input { input: Vec<u8>, ptr: usize }
struct Data { data: Vec<u8>, ptr: usize }
struct Code { code: Vec<char>, ptr: usize }
struct Machine { input: Input, code: Code, data: Data }

impl Input {
    fn read(&mut self) -> u8 {
        self.ptr += 1;
        self.input[self.ptr - 1]
    }
}

impl Data {
    fn write(&mut self, value: u8) { self.data[self.ptr] = value; }
    fn read(&self) -> u8 { self.data[self.ptr] }
    
    fn right(&mut self) {
        self.ptr += 1;
        if self.data.len() < self.ptr + 1 { self.data.push(0u8) };
    }
    fn left(&mut self) { self.ptr -= 1; }
    
    fn incr(&mut self) { self.data[self.ptr] = self.data[self.ptr].wrapping_add(1u8); }
    fn decr(&mut self) { self.data[self.ptr] = self.data[self.ptr].wrapping_sub(1u8); }    
}

impl Code {  
    fn at(&self, ptr: usize) -> char { self.code[ptr] }
    fn read(&self) -> char { self.at(self.ptr) }
}

impl Machine {
    fn new(input: Vec<u8>, code: &str) -> Self {
        Machine {
          input: Input { input: input, ptr: 0 },
          code: Code { code: code.chars().collect(), ptr: 0 },
          data: Data { data: vec![0u8], ptr: 0 }
        }
    }
    
    fn go(&mut self) -> Vec<u8> {
        let mut output: Vec<u8> = Vec::new();
        while self.code.code.len() > self.code.ptr {
            match self.code.read() {
                '>' => self.right(),
                '<' => self.left(),
                '+' => self.incr(),
                '-' => self.decr(),
                '.' => output.push(self.read()),
                ',' => self.write(),
                '[' => self.jzr(),
                ']' => self.jnzl(),
                _   => panic!("Unsupported language version. Please update.")
            }
            self.code.ptr += 1;
        }
        output
    }
    
    fn write(&mut self) { self.data.write(self.input.read()); }
    
    fn read(&self) -> u8 { self.data.read() }
    
    fn right(&mut self) { self.data.right(); }
    fn left(&mut self) { self.data.left(); }
    
    fn incr(&mut self) { self.data.incr(); }
    fn decr(&mut self) { self.data.decr(); }
    
    fn jzr(&mut self) {
        if self.read() != 0u8 { return; };
        let mut skips = 0;
        for i in self.code.ptr..(self.code.code.len() + 1) {
          if self.code.at(i) == '[' { skips += 1 };
          if self.code.at(i) == ']' { skips -= 1 };
          if skips == 0 { self.code.ptr = i; break; }
        }
    }
    
    fn jnzl(&mut self) {
        if self.read() == 0u8 { return; };
        let mut skips = 0;
        for i in (0..(self.code.ptr + 1)).rev() {
          if self.code.at(i) == ']' { skips += 1 };
          if self.code.at(i) == '[' { skips -= 1 };
          if skips == 0 { self.code.ptr = i; break; }
        }
    }
}